﻿using System.Security.Cryptography;
using gixoom.Dom.Attributes;
using gixoom.Dom.Block;
using gixoom.Dom.Converters;
using gixoom.Dom.Score;
using gixoom.Dom.Storage.Block;
using gixoom.Dom.Storage.File;
using gixoom.Initiator.WcfWsHttp;
using Ninject;
using Ninject.Modules;

namespace gixoom.Dom
{
    public class gixoomDomModule: NinjectModule
    {
        private const int defaultBlockSizeBytes = 65520;
        private const long longDefaultBlockSizeBytes = defaultBlockSizeBytes;

        private string[] addresses;

        /// <summary>
        /// Creates gixoomDomModule object.
        /// </summary>
        /// <param name="addresses">host addresses</param>
        /// <exception cref="System.ArgumentNullException"><i>addresses</i> is a null reference.</exception>
        public gixoomDomModule(string[] addresses)
        {
            Ensure.ArgumentNotNull(addresses, "addresses");

            this.addresses = addresses;
        }

        public override void Load()
        {
            Bind<IKernel>().ToConstant(Kernel);
            Bind<SHA512>().To<SHA512Managed>().InSingletonScope();
            Bind<IScoreFacade>().To<Sha512HashFacade>().InSingletonScope();
            Bind<ISha512StringValidator>().To<Sha512StringValidator>().InSingletonScope();
            Bind<IHexDigitsStringToBytesConverter>().To<HexDigitsStringToBytesConverter>().
                InSingletonScope();
            Bind<IBlockFacade>().To<BlockFacade>().InSingletonScope();
            Bind<IFileStorageFacade>().To<FileStorageFacade>().InSingletonScope();
            Bind<IFileStorage>().To<FileStorage>();

            Bind<int>().ToConstant(defaultBlockSizeBytes).
                WhenTargetHas<DefaultBlockSizeBytesAttribute>();
            Bind<long>().ToConstant(longDefaultBlockSizeBytes).
                WhenTargetHas<DefaultBlockSizeBytesAttribute>();

            Bind<IBlockIdToFileNameMapping>().To<FileNameByBlockIdDictionary>().InSingletonScope();

            var wcfWsHttpInitiatorFacade = CreategixoomWcfWsHttpInitiatorFacade();
            Bind<IgixoomWcfWsHttpInitiatorFacade>().ToConstant(wcfWsHttpInitiatorFacade);

            var blockStorageFacade = BindBlockStorageFacade(Kernel, wcfWsHttpInitiatorFacade);
            Bind<ISourceBlockStorage>().To<SourceBlockStorage>().InSingletonScope();

            if (addresses.Length > 0)
            {
                BindSourceBlockStorage(blockStorageFacade, wcfWsHttpInitiatorFacade, addresses);
            }
        }

        private IBlockStorageFacade BindBlockStorageFacade(IKernel kernel,
            IgixoomWcfWsHttpInitiatorFacade wcfWsHttpInitiatorFacade)
        {
            var facade = new BlockStorageFacade(kernel, wcfWsHttpInitiatorFacade);
            Bind<IBlockStorageFacade>().ToConstant(facade);

            return facade;
        }

        private IgixoomWcfWsHttpInitiatorFacade CreategixoomWcfWsHttpInitiatorFacade()
        {
            var module = new gixoomWcfWsHttpInitiatorModule();
            var kernel = new StandardKernel(module);
            var facade = new gixoomWcfWsHttpInitiatorFacade(kernel);

            return facade;
        }

        private void BindSourceBlockStorage(IBlockStorageFacade blockStorageFacade,
            IgixoomWcfWsHttpInitiatorFacade wcfWsHttpInitiatorFacade, string[] addresses)
        {
            var sourceBlockStorage = blockStorageFacade.CreateSourceBlockStorage(addresses);
            Bind<IBlockStorage>().ToConstant(sourceBlockStorage).WhenInjectedInto<FileStorage>();
        }
    }
}
